package robot;

import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.HashMap;

import javax.imageio.ImageIO;

public class MouseRobot {

	private Robot robot = null;

	private HashMap<Integer, Point> potMap = new HashMap<Integer, Point>();

	private BufferedImage srcImage = null;

	private Point potPixel = new Point();

	private Point ballPixel = new Point();

	private Point calcPixel = new Point();

	private Point referPixel = new Point();

	public MouseRobot() {
		try {
			robot = new Robot();
			srcImage = ImageIO.read(MouseRobot.class
					.getResource("/img/blank_table.png"));
		} catch (Exception e) {
			e.printStackTrace();
		}

		potMap.put(KeyEvent.VK_Q, POT_POINTS[0]);
		potMap.put(KeyEvent.VK_W, POT_POINTS[1]);
		potMap.put(KeyEvent.VK_E, POT_POINTS[2]);
		potMap.put(KeyEvent.VK_A, POT_POINTS[3]);
		potMap.put(KeyEvent.VK_S, POT_POINTS[4]);
		potMap.put(KeyEvent.VK_D, POT_POINTS[5]);
	}

	public void setPotPoint(Point p) {
		potPixel.setLocation(p.x, p.y);
	}

	public Point getStandardPotPoint(int keyCode) {
		Point p = new Point();

		p.x = potMap.get(keyCode).x + referPixel.x - ORIGIN_CENTER.x;
		p.y = potMap.get(keyCode).y + referPixel.y - ORIGIN_CENTER.y;

		return p;
	}

	public void setBallPoint(Point p) {
		ballPixel.setLocation(p.x, p.y);
	}

	public void setReferPoint(Point p) {
		referPixel.setLocation(p.x, p.y);
	}

	public Point getCalcPoint() {
		double a = ballPixel.x - potPixel.x;
		double b = ballPixel.y - potPixel.y;
		double c = Math.sqrt(a * a + b * b);

		int x = (int) Math.round(a * BALL_WIDTH / c + ballPixel.x);
		int y = (int) Math.round(b * BALL_WIDTH / c + ballPixel.y);

		mouseMove(x, y);
		calcPixel.setLocation(x, y);

		return calcPixel;
	}

	public Point detectReferPoint() {
		int width = Toolkit.getDefaultToolkit().getScreenSize().width;
		int height = Toolkit.getDefaultToolkit().getScreenSize().height;

		int xMin = -LOCATE_POINTS[0].x;
		int xMax = width - LOCATE_POINTS[LOCATE_POINTS.length - 1].x - xMin;

		int yMin = -LOCATE_POINTS[0].y;
		int yMax = height - LOCATE_POINTS[LOCATE_POINTS.length - 1].y - yMin;

		Rectangle rect = new Rectangle(0, 0, width, height);
		BufferedImage compareImage = robot.createScreenCapture(rect);

		for (int y = yMin; y < yMax; y++) {
			for (int x = xMin; x < xMax; x++) {
				if (isLocationMatch(compareImage, x, y)) {
					return new Point(x + ORIGIN_CENTER.x, y + ORIGIN_CENTER.y);
				}
			}
		}

		return null;
	}

	public Point detectBallPoint(Point p) {
		BufferedImage imageA = getRealImage(p);
		BufferedImage imageB = getPartImage(p);

		Point center = new Point(BALL_WIDTH - 1, BALL_WIDTH - 1);

		int matchCnt = 0;
		int left = 0;
		for (left = center.x; left >= 0; left--) {
			int rgbA = imageA.getRGB(left, center.y);
			int rgbB = imageB.getRGB(left, center.y);

			if (rgbA == rgbB || rgbA == Color.white.getRGB()) {
				matchCnt++;
			}

			if (matchCnt >= 3) {
				break;
			}
		}
		left += matchCnt;

		matchCnt = 0;
		int right = 0;
		for (right = center.x; right < imageA.getWidth(); right++) {
			int rgbA = imageA.getRGB(right, center.y);
			int rgbB = imageB.getRGB(right, center.y);

			if (rgbA == rgbB || rgbA == Color.white.getRGB()) {
				matchCnt++;
			}

			if (matchCnt >= 3) {
				break;
			}
		}
		right -= matchCnt;

		matchCnt = 0;
		int top = 0;
		for (top = center.y; top >= 0; top--) {
			int rgbA = imageA.getRGB(center.x, top);
			int rgbB = imageB.getRGB(center.x, top);

			if (rgbA == rgbB || rgbA == Color.white.getRGB()) {
				matchCnt++;
			}

			if (matchCnt >= 3) {
				break;
			}
		}
		top += matchCnt;

		matchCnt = 0;
		int bottom = 0;
		for (bottom = center.y; bottom < imageA.getWidth(); bottom++) {
			int rgbA = imageA.getRGB(center.x, bottom);
			int rgbB = imageB.getRGB(center.x, bottom);

			if (rgbA == rgbB || rgbA == Color.white.getRGB()) {
				matchCnt++;
			}

			if (matchCnt >= 3) {
				break;
			}
		}
		bottom -= matchCnt;

		Point newCenter = new Point((left + right) / 2, (top + bottom) / 2);
		left = newCenter.x;
		right = newCenter.x;
		top = newCenter.y;
		bottom = newCenter.y;

		for (int i = newCenter.x - BALL_WIDTH / 2 - 3; i <= newCenter.x
				+ BALL_WIDTH / 2 + 3; i++) {
			for (int j = newCenter.y - BALL_WIDTH / 2 - 3; j <= newCenter.y
					+ BALL_WIDTH / 2 + 3; j++) {
				int rgbA = imageA.getRGB(i, j);
				int rgbB = imageB.getRGB(i, j);

				if (rgbA != rgbB && rgbA != Color.white.getRGB()) {
					if (left > i) {
						left = i;
					}

					if (right < i) {
						right = i;
					}

					if (top > j) {
						top = j;
					}

					if (bottom < j) {
						bottom = j;
					}
				}
			}
		}

		left = p.x - BALL_WIDTH + 2 + (left + right) / 2;
		top = p.y - BALL_WIDTH + 2 + (top + bottom) / 2;

		return new Point(left, top);
	}

	public void mouseMove(int x, int y) {
		robot.mouseMove(x, y);
	}

	private BufferedImage getRealImage(Point p) {
		Rectangle rect = new Rectangle();
		rect.setBounds(p.x - BALL_WIDTH + 1, p.y - BALL_WIDTH + 1,
				BALL_WIDTH * 2 - 1, BALL_WIDTH * 2 - 1);

		return robot.createScreenCapture(rect);
	}

	private BufferedImage getPartImage(Point realP) {
		Point p = getTransferPoint(realP);

		return srcImage.getSubimage(p.x - BALL_WIDTH + 1, p.y - BALL_WIDTH + 1,
				BALL_WIDTH * 2 - 1, BALL_WIDTH * 2 - 1);
	}

	private Point getTransferPoint(Point p) {
		Point pOut = new Point();

		pOut.x = p.x - referPixel.x + ORIGIN_CENTER.x;
		pOut.y = p.y - referPixel.y + ORIGIN_CENTER.y;

		return pOut;
	}

	private boolean isLocationMatch(BufferedImage compareImage, int xInterval,
			int yInterval) {
		for (Point p : LOCATE_POINTS) {
			if (srcImage.getRGB(p.x, p.y) != compareImage.getRGB(p.x
					+ xInterval, p.y + yInterval)) {
				return false;
			}
		}

		return true;
	}

	private static final int BALL_WIDTH = 26;

	private static final Point ORIGIN_CENTER = new Point(512, 305);

	private static final Point[] POT_POINTS = { new Point(42, 66),
			new Point(512, 64), new Point(982, 66), new Point(42, 544),
			new Point(512, 546), new Point(982, 544) };

	private static final Point[] LOCATE_POINTS = { new Point(10, 22),
			new Point(10, 42), new Point(10, 500), new Point(10, 560),
			new Point(1013, 22), new Point(1013, 42), new Point(1013, 500),
			new Point(1013, 560) };

}
